fix: tone down branch-mismatch banner#4416
Conversation
The "You're on a different branch" warning fired the moment a mismatched
thread was opened, wasn't dismissible, and treated routine branch
switching as an anomaly. Reworked per user feedback:
- Copy is now a one-line fragment: "Branch changed — was <branch>", info
variant with a git-branch icon. The consequence ("sending will
continue on the current branch") lives in the chip tooltip.
- Banner only renders on send intent (composer focused or has draft
content); passive browsing shows nothing beyond the existing
branch-picker tint. Once revealed it stays mounted for the current
mismatch so it doesn't flicker on blur.
- "Move thread here" is gone — sending already rebinds the thread.
The single action is "Restore branch", with a confirm dialog only
when the working tree has uncommitted changes.
- Dismissible, keyed on threadId + both branches, session-scoped for
now (server-persisted cross-device ack is a planned follow-up).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughBranch mismatch handling now includes deterministic keys, session-scoped dismissal, focus-aware banner visibility, a single restore action, and confirmation before switching branches when uncommitted working-tree changes exist. ChangesBranch mismatch handling
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant Composer
participant ChatView
participant AlertDialog
participant BranchCheckout
Composer->>ChatView: Select Restore branch
ChatView->>AlertDialog: Request confirmation for working-tree changes
AlertDialog->>ChatView: Confirm restore
ChatView->>BranchCheckout: Switch to thread branch
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f64a69a. Configure here.
ApprovabilityVerdict: Approved UX refinement that reduces branch-mismatch banner visibility to only when the user is drafting a message. Changes are self-contained, well-tested, and don't alter core branch detection or switching functionality. You can customize Macroscope's approvability policy. Learn more. |
Cursor Bugbot caught two real gating bugs: - ChatView autofocuses the composer on every thread open, so "composer focused" was always true and the banner showed during passive reading anyway. Draft content is now the only intent signal. - revealedBranchMismatchKey was never cleared, so a mismatch that resolved and later recurred bypassed the gate. The key now resets whenever the active mismatch changes or resolves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/web/src/components/ChatView.tsx (1)
3835-3846: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider extracting
composerHasDraftContentintoChatView.logic.ts.Every other predicate in this feature (
branchMismatchKey,shouldShowBranchMismatchBanner) is a pure, independently-tested helper inChatView.logic.ts. This "has draft content" check is left inline in the component, breaking that pattern and making it untestable without mounting the component.♻️ Suggested extraction
+// ChatView.logic.ts +export function composerDraftHasContent( + draft: { prompt: string; images: unknown[]; terminalContexts: unknown[]; elementContexts: unknown[]; previewAnnotations: unknown[]; reviewComments: unknown[] } | undefined, +): boolean { + return Boolean( + draft && + (draft.prompt.trim().length > 0 || + draft.images.length > 0 || + draft.terminalContexts.length > 0 || + draft.elementContexts.length > 0 || + draft.previewAnnotations.length > 0 || + draft.reviewComments.length > 0), + ); +}- const composerHasDraftContent = useComposerDraftStore((store) => { - const draft = store.getComposerDraft(composerDraftTarget); - return Boolean( - draft && - (draft.prompt.trim().length > 0 || - draft.images.length > 0 || - draft.terminalContexts.length > 0 || - draft.elementContexts.length > 0 || - draft.previewAnnotations.length > 0 || - draft.reviewComments.length > 0), - ); - }); + const composerHasDraftContent = useComposerDraftStore((store) => + composerDraftHasContent(store.getComposerDraft(composerDraftTarget)), + );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/ChatView.tsx` around lines 3835 - 3846, Extract the draft-content predicate from the composerHasDraftContent selector in ChatView into a pure helper in ChatView.logic.ts, alongside branchMismatchKey and shouldShowBranchMismatchBanner. Have the selector delegate to that helper while preserving all existing prompt, image, terminal-context, element-context, preview-annotation, and review-comment checks, and make the helper independently testable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@apps/web/src/components/ChatView.tsx`:
- Around line 3835-3846: Extract the draft-content predicate from the
composerHasDraftContent selector in ChatView into a pure helper in
ChatView.logic.ts, alongside branchMismatchKey and
shouldShowBranchMismatchBanner. Have the selector delegate to that helper while
preserving all existing prompt, image, terminal-context, element-context,
preview-annotation, and review-comment checks, and make the helper independently
testable.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 85e733f5-156d-4036-8819-8f410da8678b
📒 Files selected for processing (3)
apps/web/src/components/ChatView.logic.test.tsapps/web/src/components/ChatView.logic.tsapps/web/src/components/ChatView.tsx
2 upstream commits (pingdotgg#4414, pingdotgg#4416). No conflicts.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f)
…4309–pingdotgg#4488) (#187) * fix(client-runtime): keep a warm thread un-settled despite a merged/closed PR (pingdotgg#4309) Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit 193e3c6) * Fix composer context strip alignment and glass shell (pingdotgg#4404) (cherry picked from commit 6ef7aa8) * Polish iOS git progress overlay with glass effects (pingdotgg#4387) (cherry picked from commit ce467da) * Improve composer glass fallbacks (pingdotgg#4406) Co-authored-by: codex <codex@users.noreply.github.com> (cherry picked from commit 67a7b1a) * feat(web): collapse large git diffs by default to make chat more readable (pingdotgg#4409) (cherry picked from commit 51672b6) * Stop new threads inheriting checkout/branch from viewed thread (pingdotgg#4411) Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit 2f41c07) * fix: tone down branch-mismatch banner (pingdotgg#4416) Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit fc3f78f) * fix: Claude Code skills discoverable for the composer $ picker (pingdotgg#4414) Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit 6b9a598) * fix(web): keep settled threads reachable when opened directly (pingdotgg#4413) Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit bb38c33) * feat(sidebar-v2): thread snoozing (pingdotgg#4311) Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: maria <maria@kuuro.net> (cherry picked from commit 202e560) * Upgrade Clerk packages and Expo integration (pingdotgg#4440) (cherry picked from commit 5d17354) Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com> * Increase light-mode contrast for user message bubbles (pingdotgg#4441) (cherry picked from commit 15e875a) * Restore model picker layout and retain iterative test state (pingdotgg#4450) Co-authored-by: codex <codex@users.noreply.github.com> (cherry picked from commit f7cc776) * Color settled PR labels on hover (pingdotgg#4451) Co-authored-by: codex <codex@users.noreply.github.com> (cherry picked from commit a7ee309) * [codex] Fix glass hover compositing artifacts (pingdotgg#4446) Co-authored-by: codex <codex@users.noreply.github.com> (cherry picked from commit ece0508) * Add Claude Opus 5 model (pingdotgg#4472) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Theo Browne <me@t3.gg> (cherry picked from commit 41a430a) * feat(web): add collapse-all toggle to diff panel (pingdotgg#4475) (cherry picked from commit 38cfc25) * feat(web): show fast mode as a bolt instead of a "Normal" label (pingdotgg#4488) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 5719e8a) * fix(sync): preserve Droid medium-access with Auto runtime mode Keep fork-only medium-access in RuntimeMode alongside upstream Auto. Update presentation maps and drop obsolete provider kind from slug helper. Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com> * fix(sync): restore fork release and runtime-mode adaptations Keep WSL node-pty on bullseye glibc, pin releases to the fork, and stop hard-requiring upstream relay/npm publish. Scope Auto vs Medium access by provider, normalize carried modes on new threads, and fix Claude/Droid type regressions from the restack. Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com> * fix(sync): refresh lockfile after Clerk upgrade restack Align patchedDependencies hashes (react-native-screens) and importer entries with the current workspace patches so frozen CI installs succeed. Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com> --------- Co-authored-by: Theo Browne <me@t3.gg> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Julius Marminge <julius0216@outlook.com> Co-authored-by: codex <codex@users.noreply.github.com> Co-authored-by: maria <maria@kuuro.net> Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com> Co-authored-by: tomlit <tomlit@gmail.com> Co-authored-by: jan <hi@4bs3nt.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com>

What Changed
Reworks the "You're on a different branch" composer banner added in #2284, which fired on thread open, wasn't dismissible, and alarmed users doing routine branch switching (user report).
Branch changed — was <branch>, info variant, git-branch icon. No description block; the consequence ("sending will continue on the current branch") lives in the branch chip's tooltip. The current branch is never named — it's already visible in the branch picker below.resolveThreadMetadataUpdateForNextTurn, so the button duplicated the default outcome. The remaining action is Restore branch (checkout the thread's branch).threadId + threadBranch + currentBranchmismatch for the session. If the checkout later moves to a different branch, the banner is eligible again.Mismatch detection (
resolveLocalCheckoutBranchMismatch) is unchanged, as is the branch-picker tint/popover from #2284.Design doc with mockups and the reasoning (including why we deliberately did not auto-sync
thread.branchon turn completion): https://3vj3y2v9zb4u.postplan.devWhy
The mismatch is valid but the old presentation punished the most common innocent flow: finish issue A, checkout a branch for issue B, reopen thread A just to read context → big non-dismissible amber warning at thread-open time, when the only actual risk exists at send time.
Follow-up (separate PR)
Server-persisted dismissal ack (
{threadBranch, checkoutBranch}on thread metadata) so dismissals hold across devices; session-scoped for now.Testing
ChatView.logic.test.ts)bun run typecheck,bun run lint, fullapps/webunit suite (1495 tests) all passChecklist
🤖 Generated with Claude Code
Note
Low Risk
Composer UX and when the banner renders; restore still uses existing git checkout and thread metadata RPCs, with no change to mismatch detection itself.
Overview
Reworks the local checkout vs thread branch composer banner so it no longer appears on every thread open.
Intent gating: New
shouldShowBranchMismatchBannerlogic only shows the notice when the composer has draft content (or after it was already shown for the same mismatch, so clearing the draft does not flicker it away). Passive reading stays quiet aside from existing branch-picker cues.Copy and actions: The banner switches from a warning with two repair buttons to an info line (
Branch changed — was <thread branch>) with tooltip context. Move thread here is removed; Restore branch checks out the thread’s branch (with an AlertDialog when the working tree is dirty).Dismissal: Per-mismatch keys (
threadId:threadBranch:currentBranch) can be dismissed for the session via a module-level set inChatView.logic.ts, with unit tests for gating, keys, and dismissal.Reviewed by Cursor Bugbot for commit b40c0f1. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Reduce branch-mismatch banner intrusiveness in ChatView
Setin ChatView.logic.ts.AlertDialogconfirmation prompt appears before switching branches.GitBranchIcon.Macroscope summarized b40c0f1.
Summary by CodeRabbit
New Features
Bug Fixes